import type { Context, Page } from '@/types'
import type { PageTransformer } from './types'
import type { Operation } from '@/rest/components/types'
import { renderContent } from '@/content-render/index'
import matter from '@gr2m/gray-matter'
import { readFileSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
import { fastTextOnly } from '@/content-render/unified/text-only'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

/**
 * Transformer for REST API pages
 * Converts REST operations and their data into markdown format using a Liquid template
 */
export class RestTransformer implements PageTransformer {
  canTransform(page: Page): boolean {
    // Only transform REST pages that are not landing pages
    // Landing pages (like /en/rest) will be handled by a separate transformer
    return page.autogenerated === 'rest' && !page.relativePath.endsWith('index.md')
  }

  async transform(
    page: Page,
    pathname: string,
    context: Context,
    apiVersion?: string,
  ): Promise<string> {
    // Import getRest dynamically to avoid circular dependencies
    const { default: getRest } = await import('@/rest/lib/index')

    // Extract version from context
    const currentVersion = context.currentVersion!

    // Use the provided apiVersion, or fall back to the latest from context
    const effectiveApiVersion =
      apiVersion ||
      (context.currentVersionObj?.apiVersions?.length
        ? context.currentVersionObj.latestApiVersion
        : undefined)

    // Parse the category and subcategory from the page path
    // e.g. /en/rest/actions/artifacts -> category: actions, subcategory: artifacts
    const pathParts = pathname.split('/').filter(Boolean)
    const restIndex = pathParts.indexOf('rest')

    if (restIndex === -1 || restIndex >= pathParts.length - 1) {
      throw new Error(`Invalid REST path: ${pathname}`)
    }

    const category = pathParts[restIndex + 1]
    const subcategory = pathParts[restIndex + 2] // May be undefined for category-only pages

    // Get the REST operations data
    const restData = await getRest(currentVersion, effectiveApiVersion)

    let operations: Operation[] = []

    if (subcategory && restData[category]?.[subcategory]) {
      operations = restData[category][subcategory]
    } else if (category && restData[category]) {
      // For categories without subcategories, operations are nested directly
      const categoryData = restData[category]
      // Flatten all operations from all subcategories
      operations = Object.values(categoryData).flat()
    }

    // Prepare manual content
    let manualContent = ''
    if (page.markdown) {
      const markerIndex = page.markdown.indexOf(
        '<!-- Content after this section is automatically generated -->',
      )
      if (markerIndex > 0) {
        const { content } = matter(page.markdown)
        const manualContentMarkerIndex = content.indexOf(
          '<!-- Content after this section is automatically generated -->',
        )
        if (manualContentMarkerIndex > 0) {
          const rawManualContent = content.substring(0, manualContentMarkerIndex).trim()
          if (rawManualContent) {
            manualContent = await renderContent(rawManualContent, {
              ...context,
              markdownRequested: true,
            })
          }
        }
      }
    }

    // Prepare data for template
    const templateData = await this.prepareTemplateData(
      page,
      operations,
      context,
      manualContent,
      effectiveApiVersion,
    )

    // Load and render template
    const templatePath = join(__dirname, '../templates/rest-page.template.md')
    const templateContent = readFileSync(templatePath, 'utf8')

    // Render the template with Liquid
    const rendered = await renderContent(templateContent, {
      ...context,
      ...templateData,
      markdownRequested: true,
    })

    return rendered
  }

  /**
   * Prepare data for the Liquid template
   */
  private async prepareTemplateData(
    page: Page,
    operations: Operation[],
    context: Context,
    manualContent: string,
    apiVersion?: string,
  ): Promise<Record<string, any>> {
    // Prepare page intro
    const intro = page.intro ? await page.renderProp('intro', context, { textOnly: true }) : ''

    // Prepare operations for the template
    const preparedOperations = await Promise.all(
      operations.map(async (operation) => await this.prepareOperation(operation)),
    )

    return {
      page: {
        title: page.title,
        intro,
      },
      manualContent,
      restOperations: preparedOperations,
      apiVersion,
    }
  }

  /**
   * Prepare a single operation for template rendering
   */
  private async prepareOperation(operation: Operation): Promise<Record<string, any>> {
    // Convert HTML description to text
    const description = operation.descriptionHTML ? fastTextOnly(operation.descriptionHTML) : ''

    // Determine header settings
    const needsContentTypeHeader = operation.subcategory === 'inference'
    const omitHeaders =
      operation.subcategory === 'management-console' || operation.subcategory === 'manage-ghes'
    const showHeaders = !omitHeaders

    // Check if operation has parameters
    const hasParameters =
      (operation.parameters?.length || 0) > 0 || (operation.bodyParameters?.length || 0) > 0

    // Process status codes to convert HTML descriptions to plain text
    const statusCodes = operation.statusCodes?.map((statusCode) => ({
      ...statusCode,
      description: statusCode.description ? fastTextOnly(statusCode.description) : undefined,
    }))

    // Prepare code examples with processed URLs
    const codeExamples =
      operation.codeExamples?.map((example) => {
        let url = `${operation.serverUrl}${operation.requestPath}`

        // Replace path parameters in URL
        if (example.request?.parameters && Object.keys(example.request.parameters).length > 0) {
          for (const [key, value] of Object.entries(example.request.parameters)) {
            url = url.replace(`{${key}}`, String(value))
          }
        }

        return {
          request: {
            description: example.request?.description
              ? fastTextOnly(example.request.description)
              : '',
            url,
            acceptHeader: example.request?.acceptHeader,
            bodyParameters: example.request?.bodyParameters
              ? JSON.stringify(example.request.bodyParameters, null, 2)
              : null,
          },
          response: {
            statusCode: example.response?.statusCode,
            schema: (example.response as any)?.schema
              ? JSON.stringify((example.response as any).schema, null, 2)
              : null,
          },
        }
      }) || []

    return {
      ...operation,
      description,
      hasParameters,
      showHeaders,
      needsContentTypeHeader,
      statusCodes,
      codeExamples,
    }
  }
}
